package com.neuedu.common.service;

import com.alibaba.fastjson.JSON;

import com.neuedu.common.domain.enums.WebSocketEnum;
import com.neuedu.common.domain.po.Message;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Date;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * webSocket服务端
 */
@ServerEndpoint("/webSocket/{username}")
@Component
@Slf4j
public class WebSocketServer {
    //静态变量，用来记录当前在线连接数。应该把它设计成线程安全的。
    private static AtomicInteger onlineNum = new AtomicInteger();

    //concurrent包的线程安全Set，用来存放每个客户端对应的WebSocketServer对象。
    // --------------- 同一个通道连接状态只能连接一次 start ------------------------
    private static ConcurrentHashMap<String, Session> sessionPools = new ConcurrentHashMap<>();

    // 判断是否是新连接
    private static Boolean isNewConnected = true;
    // 暂存新连接session
    private static Session  sessionNew = null;
    //------------------- 同一个通道只能连接一次 end ------------------------------

    //发送消息
    public void sendMessage(Session session, String message) throws IOException {
        // 上线后给每个在线的用户或设备发送消息 通知已上线
        if (session != null) {
            synchronized (session) {
                System.out.println("发送数据：" + message);
                session.getBasicRemote().sendText(message);
                // 确定通道打开后再发消息
//                if (session.isOpen()) {
//                    try {
//                        session.getBasicRemote().sendText(message);
//                    } catch (IOException e) {
//                        // 处理异常情况
//                        e.printStackTrace();
//                    }
//                }
            }
        }
    }

    //给指定用户发送信息
    public void sendInfo(String userName, String message) {
        Session session = sessionPools.get(userName);
        try {
            sendMessage(session, message);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 群发消息
    public void broadcast(String message) {
        for (Session session : sessionPools.values()) {
            try {
                sendMessage(session, message);
            } catch (Exception e) {
                e.printStackTrace();
                continue;
            }
        }
    }

    //建立连接成功调用
    @OnOpen
    public void onOpen(Session session, @PathParam(value = "username") String userName) throws IOException {
        Boolean isExist = false;
        if (sessionPools.containsKey(userName)) {
            isExist = true;
            isNewConnected =  false;
            sessionNew = session;
            session.close();
        }
        if (!isExist) {
            isNewConnected = true;
            addOnlineCount();
            sessionPools.put(userName, session);
            log.info(userName + "加入webSocket！当前人数为" + onlineNum);
            // 广播上线消息
            Message msg = setMessage("上线",WebSocketEnum.TO_ONLINE.getCode(),userName,null,WebSocketEnum.SEND_GROUP.getCode());
            broadcast(JSON.toJSONString(msg, true));
        }
    }

    //关闭连接时调用
    @OnClose
    public void onClose(@PathParam(value = "username") String userName) {
        Boolean isExist = false;
        if (sessionPools.containsKey(userName)) {
            isExist = true;
            if (sessionNew != null) {
                isNewConnected = false;
            } else {
                isNewConnected = true;
            }
        }
        if (isExist && !isNewConnected) {
            sessionNew = null;
            log.info(userName + "已连接，请先断开设备");
        } else {
            // 广播下线消息
            Message msg = setMessage("下线",WebSocketEnum.TO_OFFLINE.getCode(),userName,null
                    ,WebSocketEnum.SEND_GROUP.getCode());
            sessionPools.remove(userName);
            broadcast(JSON.toJSONString(msg, true));
            subOnlineCount();
            log.info(userName + "断开webSocket连接！当前人数为" + onlineNum);
        }
    }

    //收到客户端信息后，根据接收人的username把消息推下去或者群发
    // to=-1群发消息
    @OnMessage
    public void onMessage(String message) throws IOException {
        System.out.println("server get" + message);
        Message msg;
        try {
            msg = JSON.parseObject(message, Message.class);
        } catch (Exception e) {
            log.error("格式解析错误");
            log.error(e.getMessage());
            return;
        }
        msg.setDate(new Date());
        WebSocketEnum getCode = WebSocketEnum.getByCode(msg.getSendStatus());
        switch (getCode){
            case SEND_GROUP:
                broadcast(JSON.toJSONString(msg, true));
                break;
            case SEND_ONLY:
                sendInfo(msg.getTo(), JSON.toJSONString(msg, true));
                break;
            default:
                sendSystemMessage(JSON.toJSONString(msg, true));
                break;
        }
    }

    //错误时调用
    @OnError
    public void onError(Session session, Throwable throwable) {
        log.error("发生错误");
        throwable.printStackTrace();
    }

    public static void addOnlineCount() {
        onlineNum.incrementAndGet();
    }

    public static void subOnlineCount() {
        onlineNum.decrementAndGet();
    }

    public static AtomicInteger getOnlineNumber() {
        return onlineNum;
    }

    public static ConcurrentHashMap<String, Session> getSessionPools() {
        return sessionPools;
    }

    /*
     * 发送系统消息
     */
    public void sendSystemMessage(String message) {
        System.out.println("发送系统消息：" +message);
    }

    /*
     * 消息体公共方法
     */
    public Message setMessage(String text, String deviceStatus, String from, String to, String sendStatus) {
        Message msg = new Message();
        msg.setDate(new Date());
        msg.setText(text);
        msg.setDeviceStatus(deviceStatus);
        msg.setFrom(from);
        if (to != null) {
            msg.setTo(to);
        }
        msg.setSendStatus(sendStatus);
        return msg;
    }
}

